package com.pkk.cloud.support.gateway.limiter.global.filter;

import static com.pkk.cloud.support.gateway.limiter.global.config.GlobalLimitConfig.GLOBAL_RATE_LIMITER;

import com.alibaba.fastjson.JSONObject;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import com.pkk.cloud.support.gateway.code.GatewayBusinessCode;
import com.pkk.cloud.support.gateway.limiter.global.properties.GlobalRateLimitProperties;
import com.pkk.cloud.support.gateway.limiter.global.util.GlobalMethodRateLimitUtil;
import com.pkk.components.rpc.enums.CodeEnum;
import com.pkk.components.rpc.response.util.ResponseUtil;
import javax.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;

/**
 * @description: 全局请求过滤器
 * @author: peikunkun
 * @create: 2020-03-24 10:54
 **/
@Slf4j
@Component
public class GlobalRateLimiterFilter extends ZuulFilter {

  @Autowired
  private GlobalRateLimitProperties globalRateLimitProperties;
  @Autowired(required = false)
  private GlobalMethodRateLimitUtil globalMethodRateLimitUtil;

  /**
   * @Description: 请求拦截前进行操作
   * @return: java.lang.String
   * @Author: peikunkun
   * @Date: 2020/3/24 0024 上午 10:56
   */
  @Override
  public String filterType() {
    return FilterConstants.PRE_TYPE;
  }


  /**
   * @Description: 拦截在普通拦截之前
   * @return: int
   * @Author: peikunkun
   * @Date: 2020/3/24 0024 上午 10:56
   */
  @Override
  public int filterOrder() {
    return -1;
  }

  /**
   * @Description: 是否开启过滤
   * @return: boolean
   * @Author: peikunkun
   * @Date: 2020/3/24 0024 上午 10:57
   */
  @Override
  public boolean shouldFilter() {
    if (null == globalRateLimitProperties) {
      return false;
    }
    return globalRateLimitProperties.isEnable();
  }

  @Override
  public Object run() throws ZuulException {
    RequestContext requestContext = RequestContext.getCurrentContext();
    HttpServletRequest request = requestContext.getRequest();

    //非堵塞尝试获取-全局配置
    if (null != GLOBAL_RATE_LIMITER && !GLOBAL_RATE_LIMITER.tryAcquire()) {
      return error(requestContext, GatewayBusinessCode.TOO_MANY_REQUESTS);
    }

    //接口限制
    final String requestURI = request.getRequestURI();
    if (StringUtils.isBlank(requestURI)) {
      return error(requestContext, GatewayBusinessCode.REQUEST_NO_URL);
    }

    //接口调用超限制
    if (null != globalMethodRateLimitUtil && !globalMethodRateLimitUtil.tryAcquire(requestURI)) {
      return error(requestContext, GatewayBusinessCode.TOO_MANY_REQUESTS);
    }
    return null;
  }


  private RequestContext error(RequestContext requestContext, CodeEnum<String, String> codeEnum) {
    return error(requestContext, codeEnum.getCode(), codeEnum.getMsg());
  }

  /**
   * @Description: 错误处理
   * @Param: requestContext
   * @return: void
   * @Author: peikunkun
   * @Date: 2020/3/24 0024 下午 3:11
   */
  private RequestContext error(RequestContext requestContext, String code, String msg) {
    // 过滤该请求，不对其进行路由
    requestContext.setSendZuulResponse(false);
    requestContext.setResponseStatusCode(Integer.valueOf(code));
    requestContext.setResponseBody(JSONObject.toJSONString(ResponseUtil.error(code, msg)));
    requestContext.getResponse().setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
    return requestContext;
  }

}
